################################### IMPORTS ###################################

from django.http import *
from django.shortcuts import get_object_or_404, render, render_to_response, redirect
from django.template import RequestContext
from django.contrib.auth.decorators import login_required

from .models import Product, Feature, Product_Score, Feature_Score, Average_Product_Score, SD_Product_Score, Average_Feature_Score, SD_Feature_Score, Review
from django.core.exceptions import ObjectDoesNotExist

from haystack.query import SearchQuerySet

import json 

import MySQLdb
import _mysql_exceptions

from math import exp, pi
from scipy.integrate import quad

import logging

################################### VIEWS ###################################

### SEARCH PRODUCTS
@login_required(login_url='/login')
def Search(request):
	logger = logging.getLogger(__name__)
	logger.debug(request.GET)
	logger.debug(request.POST)
	'''-------------------------------------------------------------------------'''		
	### GET A LIST OF THE CATEGORIES FOR THE FILTER	
	# connect to the hunter SQL database
	mysql_host = '112.124.1.3'
	mysql_port = 3306
	mysql_user = 'hunter_guest'
	mysql_passwd = 'hunter_guest'
	mysql_db = 'hunter'

	conn = MySQLdb.connect(host=mysql_host, port=mysql_port,
	user=mysql_user, passwd=mysql_passwd, db=mysql_db, charset='utf8')
	cursor = conn.cursor()
	
	# Get categories
	stmt="SELECT DISTINCT category FROM product"
	cursor.execute(stmt)
	results=cursor.fetchall()
	categories=[]
	for result in results:
		for x in result:
			categories.append(x)
		
	'''-------------------------------------------------------------------------'''		
	### BUILD SEARCH ENGINE
	# Define a class to pack all of the values needed for each product
	class Product_Values(object):
		id=''
		name=''
		category=''
		website=''
		update_date=''
		score=''
		percentile=''
		
	# define standard normal distribution in order to calculate percentiles
	def normal(x):
		return ((float(1)/float(2*pi))**.5)*float(exp(float(-.5)*float(x**2)))
	
	# Define values needed
	notice=''
	not_empty=False
	products=[]
	product_value_vector=[]
	number_of_results=''	
	
	# Get input	and assign product info to Product_Values class
	if request.GET:
		logger.debug('1')
		sTerm = request.GET['sTerm']
		sFilter=request.GET['sFilter']
		logger.debug('sTerm is: '+str(sTerm))
		logger.debug('sFilter is: '+str(sFilter))
		if sFilter=='':
			logger.debug('2')
			results=SearchQuerySet().auto_query(sTerm).order_by('-update_date')
		elif sTerm!='' and sFilter!='':
			logger.debug('3')
			results=SearchQuerySet().auto_query(sTerm).order_by('-update_date')	
			results_temp=[]
			for result in results:
				if result.category==sFilter:
					results_temp.append(result)
			results=results_temp
		else:
			logger.debug('4')
			results=SearchQuerySet().filter(category=sFilter).order_by('-update_date')
		for result in results:
			products.append(result)
		if len(products)==0:
			logger.debug('5')		
			notice='No results found.'
		else:
			logger.debug('6')
			not_empty=True
			number_of_results=str(len(products))+' results'
		for product in products:
			IDENTIFIER=product.product_id	
			product_score_object=Product_Score.objects.filter(product_id=IDENTIFIER)			
			if len(product_score_object)>0:
				product_score_object=product_score_object[0]
			value_object=Product_Values()
			value_object.id=product.id
			value_object.name=product.name
			value_object.category=product.category
			value_object.website=product.website
			value_object.update_date=product.update_date
			try:
				if product_score_object.score!=0:
					value_object.score=format(product_score_object.score, '.15f')
					
					average_object=Average_Product_Score.objects.get(category=value_object.category)
					c_mean=average_object.average_score
					sd_object=SD_Product_Score.objects.get(category=value_object.category)
					c_sd=sd_object.sd_score										
					z_value=(float(value_object.score)-float(c_mean))/float(c_sd)
					I=quad(normal, -100, z_value)
					value_object.percentile=round(100*I[0],2)
				else:
					value_object.score=None
					value_object.percentile=None				
			except ZeroDivisionError:				
				value_object.score=None
				value_object.percentile=None
			except AttributeError:
				value_object.score=None
				value_object.percentile=None				
			product_value_vector.append(value_object)
	
	'''-------------------------------------------------------------------------'''		
	### SETUP SEARCH BAR
	if request.POST:
		logger.debug('7')
		search = request.POST['search']
		url_line="/products/?csrfmiddlewaretoken=LVjD4JDIdMFwB7VYpRsenyxejVwGFzQk&sTerm="+search+"&sFilter="
		return HttpResponseRedirect(url_line)	

	'''-------------------------------------------------------------------------'''		
	### DISPLAY NAME OF USER IF LOGGED IN
	# Define values needed
	user_fname=''
	user_lname=''
	login_check=False
	
	# login check
	login_check=request.user.is_authenticated()
	if login_check:
		user_fname=request.user.first_name
		user_lname=request.user.last_name
	
	'''-------------------------------------------------------------------------'''		
	### DEFINE CONTEXT AND RENDER		
	context={'product_value_vector' : product_value_vector, 'notice' : notice, 'not_empty' : not_empty, 'number_of_results' : number_of_results, 'categories' : categories, 'user_fname' : user_fname, 'user_lname' : user_lname, 'login_check' : login_check}
	return render_to_response('products/search.html', context, context_instance=RequestContext(request))

### PRODUCT INFORMATION
@login_required(login_url='/login')
def Information(request, product_id):
	'''-------------------------------------------------------------------------'''		
	### RETRIEVE PRODUCT INFORMATION
	# define standard normal distribution in order to calculate percentiles
	def normal(x):
		return ((float(1)/float(2*pi))**.5)*float(exp(float(-.5)*float(x**2)))
	
	# Get product	
	product=get_object_or_404(Product, pk=product_id)

	# Get product score
	IDENTIFIER=product.product_id	
	product_score_object=get_object_or_404(Product_Score, product_id=IDENTIFIER)	
	try:
		if product_score_object.score!=0:
			product_score=format(product_score_object.score, '.15f')
		else:
			product_score=None
	except AttributeError:
		product_score=None
		
	# Calculate percentile of product score
	try:
		average_object=Average_Product_Score.objects.get(category=product.category)
		c_mean=average_object.average_score
		sd_object=SD_Product_Score.objects.get(category=product.category)
		c_sd=sd_object.sd_score										
		z_value=(float(product_score_object.score)-float(c_mean))/float(c_sd)
		I=quad(normal, -100, z_value)
		product_percentile=round(100*I[0],2)				
	except ZeroDivisionError:				
		product_percentile=None
	except AttributeError:
		product_percentile=None	

	'''-------------------------------------------------------------------------'''		
	### FEATURE INFORMATION
	# Define a class to pack all of the values needed for each feature
	class Feature_Values(object):
		product_id=''
		feature=''
		category=''
		score=''
		percentile=''
	
	# Get feature scores set for the product
	IDENTIFIER=product.product_id	
	feature_set=Feature_Score.objects.filter(product_id=IDENTIFIER)
	
	# Assign values to Features_Values class
	feature_value_vector=[]
	for feature in feature_set:
		value_object=Feature_Values()
		value_object.product_id=feature.product_id
		value_object.feature=feature.feature
		value_object.category=feature.category
		value_object.score=feature.score
		if value_object.score!=0:
			average_object=Average_Feature_Score.objects.get(feature=value_object.feature, category=value_object.category)
			cf_mean=average_object.average_score
			sd_object=SD_Feature_Score.objects.get(feature=value_object.feature, category=value_object.category)
			cf_sd=sd_object.sd_score
			try: 
				z_value=(float(value_object.score)-float(cf_mean))/float(cf_sd)
				I=quad(normal, -100, z_value)
				percentile=round(100*I[0],2)
			except ZeroDivisionError:
				percentile=None	
			value_object.percentile=percentile	
		else:
			value_object.score=None
			value_object.percentile=None		
		feature_value_vector.append(value_object)

	'''-------------------------------------------------------------------------'''		
	### GET THE TOP 10 MOST IMPACTFUL FEATURES FOR THE CHARTS
	# Get a list with all the feature scores
	all_feature_scores=[]
	for feature in feature_set:
		all_feature_scores.append(feature.score)	
			
	# Retrieve feature score objects
	abs_value_scores=[]
	for score in all_feature_scores:
		abs_score=abs(score)
		abs_value_scores.append(abs_score)
	abs_value_scores=sorted(abs_value_scores)
	top_abs_value_scores=abs_value_scores[-10:]
	
	impact_features=[]
	for feature in feature_set:
		if abs(feature.score) in top_abs_value_scores:
			impact_features.append(feature)
	
	# get order names, scores and percentiles for the most impactful features
	impact_feature_names=[]		
	impact_feature_scores=[]
	impact_feature_percentiles=[]
	for feature in impact_features:
		if feature.score==0:
			continue
		impact_feature_names.append(feature.feature)
		impact_feature_scores.append(feature.score)
		for value in feature_value_vector:
			if value.feature==feature.feature:
				impact_feature_percentiles.append(value.percentile)
		
	# Convert values with json so they are compatible with highcharts
	impact_feature_names=json.dumps(impact_feature_names)
	impact_feature_scores=json.dumps(impact_feature_scores)
	impact_feature_percentiles=json.dumps(impact_feature_percentiles)

	'''-------------------------------------------------------------------------'''			
	### DISPLAY NAME OF USER IF LOGGED IN
	# Define values needed
	user_fname=''
	user_lname=''
	login_check=False
	
	# login check
	login_check=request.user.is_authenticated()
	if login_check:
		user_fname=request.user.first_name
		user_lname=request.user.last_name

	'''-------------------------------------------------------------------------'''		
	### SETUP SEARCH BAR
	if request.POST:
		search = request.POST['search']
		url_line="/products/?csrfmiddlewaretoken=LVjD4JDIdMFwB7VYpRsenyxejVwGFzQk&sTerm="+search+"&sFilter="
		return HttpResponseRedirect(url_line)	

	'''-------------------------------------------------------------------------'''		
	### RENDER
	return render(request, 'products/info.html', {'product': product, 'product_score' : product_score, 'product_percentile' : product_percentile, 'feature_value_vector' : feature_value_vector, 'impact_feature_names' : impact_feature_names, 'impact_feature_scores' : impact_feature_scores, 'impact_feature_percentiles' : impact_feature_percentiles, 'user_fname' : user_fname, 'user_lname' : user_lname, 'login_check' : login_check})

### PRODUCT REVIEWS
@login_required(login_url='/login')
def Product_Reviews(request, product_id):
	'''-------------------------------------------------------------------------'''		
	### Retrieve Product
	product=get_object_or_404(Product, pk=product_id)
	
	'''-------------------------------------------------------------------------'''		
	### GET THE SET OF REVIEWS
	#Get reviews for the product
	IDENTIFIER=product.product_id
	review_set=Review.objects.filter(product_id=IDENTIFIER)
	
	#Get number of reviews for the product
	number_of_reviews=len(review_set)

	'''-------------------------------------------------------------------------'''		
	### DISPLAY THE NAME OF USER IF LOGGED IN
	# Define values needed
	user_fname=''
	user_lname=''
	login_check=False
	
	# login check
	login_check=request.user.is_authenticated()
	if login_check:
		user_fname=request.user.first_name
		user_lname=request.user.last_name

	'''-------------------------------------------------------------------------'''		
	### SETUP SEARCH BAR
	if request.POST:
		search = request.POST['search']
		url_line="/products/?csrfmiddlewaretoken=LVjD4JDIdMFwB7VYpRsenyxejVwGFzQk&sTerm="+search+"&sFilter="
		return HttpResponseRedirect(url_line)	

	'''-------------------------------------------------------------------------'''		
	### RENDER
	return render(request, 'products/reviews.html', {'product': product, 'review_set' : review_set, 'user_fname' : user_fname, 'user_lname' : user_lname, 'login_check' : login_check})